1 module hip.hipaudio.backend.xaudio.source;
2 
3 version(Windows):
4 version(DirectX):
5 
6 import hip.hipaudio.backend.xaudio.player;
7 import hip.hipaudio.backend.xaudio.clip;
8 import hip.hipaudio.audiosource;
9 import directx.xaudio2;
10 import directx.win32;
11 import hip.util.system:getWindowsErrorMessage;
12 import hip.error.handler;
13 
14 enum WAVE_FORMAT_IEEE_FLOAT = 0x0003;
15 
16 class HipXAudioSource : HipAudioSource
17 {
18     IXAudio2SourceVoice sourceVoice;
19     protected bool isClipDirty = true;
20 
21     this(HipXAudioPlayer player)
22     {
23 
24         AudioConfig cfg = player.config;
25         WAVEFORMATEX fmt;
26         WORD format;
27 
28         switch(cfg.getBitDepth)
29         {
30             case 8:
31             case 16:
32                 format = WAVE_FORMAT_PCM;
33                 break;
34             case 32:
35                 format = WAVE_FORMAT_IEEE_FLOAT;
36                 break;
37             default:
38                 ErrorHandler.assertExit(false, "XAudio2 Does not support the current bit depth");
39                 break;
40         }
41         fmt.wFormatTag      = format;
42         fmt.nChannels       = cast(ushort)cfg.channels;
43         fmt.nAvgBytesPerSec = cfg.sampleRate * (cfg.getBitDepth/8) * cfg.channels;
44         fmt.nSamplesPerSec  = cfg.sampleRate;
45         fmt.nBlockAlign     = cast(ushort)(cfg.channels * cfg.getBitDepth())/8;
46         fmt.wBitsPerSample  = cast(ushort)cfg.getBitDepth;
47         fmt.cbSize          = 0;
48         HRESULT hr = player.xAudio.CreateSourceVoice(&sourceVoice, &fmt);
49 
50         ErrorHandler.assertLazyExit(SUCCEEDED(hr), "Could not create source voice: \n\t"~HipXAudioPlayer.getError(hr));
51         
52     }
53     alias clip = HipAudioSource.clip;
54 
55 
56     protected void submitClip()
57     {
58         XAUDIO2_BUFFER* buffer = getBufferFromAPI(clip).xaudio;
59         if(isClipDirty)
60         {
61             isClipDirty = false;
62             HRESULT hr = sourceVoice.SetSourceSampleRate(clip.getSampleRate());
63             ErrorHandler.assertLazyExit(SUCCEEDED(hr),
64             "Could not set source voice Sample Rate:\n\t"~HipXAudioPlayer.getError(hr));
65         }
66 
67         HRESULT hr = sourceVoice.SubmitSourceBuffer(buffer, null);
68         ErrorHandler.assertLazyExit(SUCCEEDED(hr),
69         "Could not submit XAudio2 source voice:\n\t"~HipXAudioPlayer.getError(hr));
70     }
71 
72     override IHipAudioClip clip(IHipAudioClip newClip)
73     {
74         if(newClip != clip)
75             isClipDirty = true;
76         super.clip(newClip);
77         return newClip;
78     }
79 
80     alias loop = HipAudioSource.loop;
81     override bool loop(bool value)
82     {
83         bool ret = super.loop(value);
84         HipXAudioClip c = clip.getAudioClipBackend!(HipXAudioClip);
85         c.buffer.LoopCount = loop ? XAUDIO2_LOOP_INFINITE : 0;
86         return ret;
87     }
88 
89     
90         
91     override bool play()
92     {
93         if(isPlaying)
94         {
95             sourceVoice.Stop();
96             sourceVoice.FlushSourceBuffers();
97         }
98         submitClip();
99         
100         HRESULT hr = sourceVoice.Start(0);
101         ErrorHandler.assertLazyExit(SUCCEEDED(hr), "XAudio2 Failed to play: \n\t"~HipXAudioPlayer.getError(hr));
102         isPlaying = true;        
103         return SUCCEEDED(hr);
104     }
105     override bool stop()
106     {
107         //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too.
108         auto hr = sourceVoice.Stop(XAUDIO2_PLAY_TAILS);
109         ///Makes it return to 0
110         sourceVoice.FlushSourceBuffers();
111         debug
112             ErrorHandler.assertLazyErrorMessage(SUCCEEDED(hr), "XAudio2 stop failure", HipXAudioPlayer.getError(hr));
113 
114         isPlaying = false;
115         return SUCCEEDED(hr);
116     }
117     override bool pause()
118     {
119         isPaused = true;
120         //May need to use XAUDIO2_PLAY_TAILS for outputting reverb too.
121         return SUCCEEDED(sourceVoice.Stop(XAUDIO2_PLAY_TAILS));
122     }
123     override bool play_streamed() => false;
124     
125 
126     ~this()
127     {
128         if(sourceVoice !is null)
129         {
130             sourceVoice.DestroyVoice();
131             sourceVoice = null;
132         }
133     }
134 }